package com.majie.utils.utils;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * 条形码和二维码的生成和解码
 * 配合 ImageUtils可以生成带log的二维码
 */
public class ZxingHandler {

    private static final String FORMAT = "png";// 默认二维码文件格式
    private static final String CHARACTER_SET = "utf-8";// 默认二维码文件格式


    /**
     * 二维码编码参数
     */
    private static final Map<EncodeHintType, Object> HINTS = new HashMap<>();

    static {
        HINTS.put(EncodeHintType.CHARACTER_SET, CHARACTER_SET);// 字符编码
        HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 容错等级 L、M、Q、H 其中 L 为最低, H 为最高
        HINTS.put(EncodeHintType.MARGIN, 2);// 二维码与图片边距
    }


    /**
     * 条形码编码
     *
     * @param contents 编码文本内容
     * @param width    宽
     * @param height   高
     * @param imgPath  图片保存路径
     */
    public static void barCodeWriteToPath(String contents, int width, int height, String imgPath) {
        int codeWidth = 3 + // start guard
                (7 * 6) + // left bars
                5 + // middle guard
                (7 * 6) + // right bars
                3; // end guard
        codeWidth = Math.max(codeWidth, width);
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
                    BarcodeFormat.EAN_13, codeWidth, height, null);

            MatrixToImageWriter
                    .writeToPath(bitMatrix, FORMAT, new File(imgPath).toPath());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条形码解码
     *
     * @param imgPath 图片保存路径
     * @return String
     */
    public static String barCodeDecodeFromPath(String imgPath) {
        BufferedImage image;
        Result result;
        try {
            image = ImageIO.read(new File(imgPath));
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

            result = new MultiFormatReader().decode(bitmap, null);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 二维码编码
     *
     * @param content 编码文本内容
     * @param width   宽
     * @param height  高
     * @param imgPath 图片保存路径
     */
    public static void qRCodeWriteToPath(String content, int width, int height, String imgPath) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            MatrixToImageWriter.writeToPath(bitMatrix, FORMAT, new File(imgPath).toPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 二维码解码
     *
     * @param imgPath 图片保存路径
     * @return String
     */
    public static String qRCodeDecodeFromPath(String imgPath) {
        BufferedImage image;
        Result result;
        try {
            image = ImageIO.read(new File(imgPath));
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
            hints.put(DecodeHintType.CHARACTER_SET, CHARACTER_SET);
            result = new MultiFormatReader().decode(bitmap, hints);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将二维码图片输出到一个流中
     *
     * @param content 二维码内容
     * @param stream  输出流
     * @param width   宽
     * @param height  高
     */
    public static void qRCodeWriteToStream(String content, OutputStream stream, int width, int height) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            MatrixToImageWriter.writeToStream(bitMatrix, FORMAT, stream);
        } catch (WriterException | IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回一个 BufferedImage 对象
     *
     * @param content 二维码内容
     * @param width   宽
     * @param height  高
     */
    public static BufferedImage qRCodeWriteToBufferedImage(String content, int width, int height) {
        BitMatrix bitMatrix;
        try {
            bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, HINTS);
            return MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {

        // 条形码
        String imgPath = "banner1.jpg";
        String contents = "6923450657713";
        int width = 105, height = 50;

        ZxingHandler.barCodeWriteToPath(contents, width, height, imgPath);
        System.out.println("finished zxing EAN-13 encode.");

        String decodeContent = ZxingHandler.barCodeDecodeFromPath(imgPath);
        System.out.println("解码内容如下：" + decodeContent);
        System.out.println("finished zxing EAN-13 decode.");

        // 二维码
        String imgPath2 = "banner2.jpg";
        String contents2 = "Hello Gem, welcome to Zxing!";
        int width2 = 300, height2 = 300;

        ZxingHandler.qRCodeWriteToPath(contents2, width2, height2, imgPath2);
        System.out.println("finished zxing encode.");

        String decodeContent2 = ZxingHandler.qRCodeDecodeFromPath(imgPath2);
        System.out.println("解码内容如下：" + decodeContent2);
        System.out.println("finished zxing decode.");

    }

}